package com.rf.richfitwheel.advice;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rf.richfitwheel.common.exception.BusinessException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@RestControllerAdvice(basePackages = {"com.rf.richfitwheel"})
@Slf4j
public class ControllerResponseResultBodyAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;


    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<String> handleException(MethodArgumentNotValidException e) {
        e.printStackTrace();
        List<String> msgList = e.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.toList());
        log.error("参数校验异常: {}", msgList);
        return Result.failure(HttpStatus.BAD_REQUEST.value(), msgList.toString());
    }

    @ExceptionHandler(BusinessException.class)
    public Result<String> handleException(BusinessException e) {
        e.printStackTrace();
        log.error("系统业务异常: {}", e.getMessage());
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
    }

    @ExceptionHandler(ConstraintViolationException.class)
    public Result<String> handleException(ConstraintViolationException e) {
        e.printStackTrace();
        log.error("约束违规异常: {}", e.getMessage());
        List<String> msgList = e.getConstraintViolations().stream().map(ConstraintViolation::getMessageTemplate).collect(Collectors.toList());
        return Result.failure(HttpStatus.BAD_REQUEST.value(), msgList.toString());
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Result<String> handleException(HttpMediaTypeNotSupportedException e) {
        e.printStackTrace();
        log.error("请求媒体类型异常: {}", e.getMessage());
        return Result.failure(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), HttpStatus.UNSUPPORTED_MEDIA_TYPE.getReasonPhrase());
    }

    @ExceptionHandler(value = {MissingPathVariableException.class, MissingServletRequestParameterException.class})
    public Result<String> handleException(MissingPathVariableException e) {
        e.printStackTrace();
        log.error("请求参数缺失异常: {}", e.getMessage());
        return Result.failure(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), "请求参数缺失：" + e.getMessage());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result<String> handleException(HttpRequestMethodNotSupportedException e) {
        e.printStackTrace();
        log.error("请求方式异常: {}", e.getMessage());
        return Result.failure(HttpStatus.BAD_REQUEST.value(), "请求方式错误，请确认请求方式!!!");
    }

    @ExceptionHandler(ArrayIndexOutOfBoundsException.class)
    public Result<String> handleException(ArrayIndexOutOfBoundsException e) {
        e.printStackTrace();
        log.error("ArrayIndexOutOfBounds异常: {}", e.getMessage());
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "请注意数组长度!!!");
    }

    @ExceptionHandler(IllegalStateException.class)
    public Result<String> handleException(IllegalStateException e) {
        e.printStackTrace();
        log.error("参数异常: {}", e.getMessage());
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "请确认参数是否正确!!!");
    }

    @ExceptionHandler(NullPointerException.class)
    public Result<String> handleException(NullPointerException e) {
        e.printStackTrace();
        log.error("空指针异常: {}", e.getMessage());
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "程序内部错误，请联系相关人员处理!!!");
    }

    @ExceptionHandler(ArithmeticException.class)
    public Result<String> handleException(ArithmeticException e) {
        e.printStackTrace();
        log.error("计算异常: {}", e.getMessage());
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "计算问题，请联系相关人员处理!!!");
    }

    @ExceptionHandler(value = {Exception.class, RuntimeException.class})
    public Result<String> handleException(Exception e) {
        e.printStackTrace();
        log.error("运行时异常: {}", e.getMessage());
        return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), "程序内部错误，请联系相关人员处理!!!");
    }

    @ExceptionHandler(AsyncRequestTimeoutException.class)
    public Result<String> handleException(AsyncRequestTimeoutException e) {
        e.printStackTrace();
        log.error("运行时超时异常: {}", e.getMessage());
        return Result.failure(HttpStatus.BAD_REQUEST.value(), "程序内部错误，请联系相关人员处理!!!");
    }

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        Class<?> returnClass = returnType.getMethod().getReturnType();
        if (body instanceof Exception) {
            System.out.println("returnClass = " + returnClass);
        }
//        if (MediaType.APPLICATION_JSON_VALUE.equals(selectedContentType.toString())) {
//            return body;
//        }
        if (body instanceof String || Objects.equals(returnClass, String.class)) {
            return objectMapper.writeValueAsString(Result.success(body));
        }
        if (body instanceof Result) {
            return body;
        }
        return Result.success(body);
    }
}
